#include "clkDirect3D11.h"

clkDirect3D11::clkDirect3D11()
{
	m_dxgiSwapChain = 0;
	m_d3dDevice = 0;
	m_d3dDevcon = 0;
	m_d3dFeatureLevel = (D3D_FEATURE_LEVEL)0;
	m_d3dBackBuffer = 0;
	m_d3dDepthBuffer = 0;
	m_d3dBackBufferTexture = 0;
	m_d3dDepthBufferTexture = 0;
	m_dtkSpriteBatch = 0;
}

clkDirect3D11::~clkDirect3D11()
{
	shutDirect3D11();
}

void clkDirect3D11::initDirect3D11(
	HWND a_hwnd,
	V2DF a_dimension,
	bool a_windowed,
	bool a_vsync)
{
	//define swap chain properties
	DXGI_SWAP_CHAIN_DESC scd;
	ZeroMemory(&scd,sizeof(DXGI_SWAP_CHAIN_DESC));
	scd.BufferCount       = 1;
	scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	scd.BufferUsage       = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	scd.OutputWindow      = a_hwnd;
	scd.SampleDesc.Count  = 1;
	scd.Windowed          = a_windowed;
	scd.Flags             = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

	//obtain feature level of hardware
	D3D11CreateDevice(
		0,D3D_DRIVER_TYPE_HARDWARE,0,0,0,0,
		D3D11_SDK_VERSION,0,&m_d3dFeatureLevel,0);

	//create device and swap chain
	D3D11CreateDeviceAndSwapChain(
		0,D3D_DRIVER_TYPE_HARDWARE,0,0,&m_d3dFeatureLevel,1,
		D3D11_SDK_VERSION,&scd,&m_dxgiSwapChain,
		&m_d3dDevice,0,&m_d3dDevcon);

	//obtain back buffer
	m_dxgiSwapChain->GetBuffer(0,__uuidof(ID3D11Texture2D),(void**)&m_d3dBackBufferTexture);
	m_d3dDevice->CreateRenderTargetView(m_d3dBackBufferTexture,0,&m_d3dBackBuffer);

	//create depth buffer
	D3D11_TEXTURE2D_DESC td;
	ZeroMemory(&td,sizeof(td));
	td.Width            = (UINT)a_dimension.x;
	td.Height           = (UINT)a_dimension.y;
	td.ArraySize        = 1;
	td.MipLevels        = 1;
	td.SampleDesc.Count = 1;
	td.Format           = DXGI_FORMAT_D32_FLOAT;
	td.BindFlags        = D3D11_BIND_DEPTH_STENCIL;

	D3D11_DEPTH_STENCIL_VIEW_DESC dsvd;
	ZeroMemory(&dsvd,sizeof(dsvd));
	dsvd.Format = DXGI_FORMAT_D32_FLOAT;
	dsvd.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;

	m_d3dDevice->CreateTexture2D(&td,0,&m_d3dDepthBufferTexture);
	m_d3dDevice->CreateDepthStencilView(m_d3dDepthBufferTexture,&dsvd,&m_d3dDepthBuffer);

	//set back and depth buffer
	m_d3dDevcon->OMSetRenderTargets(1,&m_d3dBackBuffer,m_d3dDepthBuffer);

	//define and set view port
	D3D11_VIEWPORT viewport;
	ZeroMemory(&viewport,sizeof(D3D11_VIEWPORT));
	viewport.TopLeftX = 0;
	viewport.TopLeftY = 0;
	viewport.MinDepth = 0.0f;
	viewport.MaxDepth = 1.0f;
	viewport.Width    = a_dimension.x;
	viewport.Height   = a_dimension.y;
	m_d3dDevcon->RSSetViewports(1,&viewport);

	//create the sprite batch
	m_dtkSpriteBatch = new SpriteBatch(m_d3dDevcon);

	//create states
	initBuffers();
	initDepthStencilStates();
	initRasterizerStates();
	initSamplerStates();

	//set default states
	depthStencilEnable();
	rasterizerSolid();
	samplerPoint();

	//set fullscreen mode
	m_d3dFullscreenMode = a_windowed;
}

void clkDirect3D11::initBuffers()
{
	//create constant buffer
	D3D11_BUFFER_DESC bd;
	ZeroMemory(&bd,sizeof(bd));
	bd.Usage     = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = sizeof(clkConstantBuffer);
	bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	m_d3dDevice->CreateBuffer(&bd,0,&m_d3dConstantBuffer);
	m_d3dDevcon->VSSetConstantBuffers(0,1,&m_d3dConstantBuffer);

	//create line vertex buffer
	D3D11_BUFFER_DESC lbdv;
	ZeroMemory(&lbdv,sizeof(lbdv));
	lbdv.Usage          = D3D11_USAGE_DYNAMIC;
	lbdv.ByteWidth      = 2*sizeof(clkVertex);
	lbdv.BindFlags      = D3D11_BIND_VERTEX_BUFFER;
	lbdv.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	m_d3dDevice->CreateBuffer(&lbdv,0,&m_d3dLineVertexBuffer);
}

void clkDirect3D11::initDepthStencilStates()
{
	//create enabled depth stencil
	D3D11_DEPTH_STENCIL_DESC dse;
	ZeroMemory(&dse,sizeof(dse));
	dse.DepthEnable                  = TRUE;
	dse.DepthWriteMask               = D3D11_DEPTH_WRITE_MASK_ALL;
	dse.DepthFunc                    = D3D11_COMPARISON_LESS;
	dse.StencilEnable                = TRUE;
	dse.StencilReadMask              = 0xFF;
	dse.StencilWriteMask             = 0xFF;
	dse.FrontFace.StencilFailOp      = D3D11_STENCIL_OP_KEEP;
	dse.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
	dse.FrontFace.StencilPassOp      = D3D11_STENCIL_OP_KEEP;
	dse.FrontFace.StencilFunc        = D3D11_COMPARISON_ALWAYS;
	dse.BackFace.StencilFailOp       = D3D11_STENCIL_OP_KEEP;
	dse.BackFace.StencilDepthFailOp  = D3D11_STENCIL_OP_DECR;
	dse.BackFace.StencilPassOp       = D3D11_STENCIL_OP_KEEP;
	dse.BackFace.StencilFunc         = D3D11_COMPARISON_ALWAYS;
	m_d3dDevice->CreateDepthStencilState(&dse,&m_d3dDepthStencilEnabled);

	//create disabled depth stencil
	D3D11_DEPTH_STENCIL_DESC dsd;
	ZeroMemory(&dsd,sizeof(dsd));
	dsd.DepthEnable = FALSE;
	dsd.StencilEnable = FALSE;
	m_d3dDevice->CreateDepthStencilState(&dsd,&m_d3dDepthStencilDisabled);
}

void clkDirect3D11::initRasterizerStates()
{
	//create solid rasterizer
	D3D11_RASTERIZER_DESC rds;
	ZeroMemory(&rds,sizeof(rds));
	rds.FillMode              = D3D11_FILL_SOLID;
	rds.CullMode              = D3D11_CULL_BACK;
	rds.FrontCounterClockwise = TRUE;
	rds.DepthClipEnable       = FALSE;
	rds.ScissorEnable         = FALSE;
	rds.MultisampleEnable     = FALSE;
	rds.AntialiasedLineEnable = FALSE;
	m_d3dDevice->CreateRasterizerState(&rds,&m_d3dRasterizerSolid);

	//create wireframe rasterizer
	D3D11_RASTERIZER_DESC rdw;
	ZeroMemory(&rdw,sizeof(rdw));
	rdw.FillMode              = D3D11_FILL_WIREFRAME;
	rdw.CullMode              = D3D11_CULL_BACK;
	rdw.FrontCounterClockwise = TRUE;
	rdw.DepthClipEnable       = FALSE;
	rdw.ScissorEnable         = FALSE;
	rdw.MultisampleEnable     = FALSE;
	rdw.AntialiasedLineEnable = FALSE;
	m_d3dDevice->CreateRasterizerState(&rdw,&m_d3dRasterizerWireframe);
}

void clkDirect3D11::initSamplerStates()
{
	D3D11_SAMPLER_DESC spp;
	ZeroMemory(&spp,sizeof(spp));
	spp.Filter         = D3D11_FILTER_MIN_MAG_MIP_POINT;
	spp.AddressU       = D3D11_TEXTURE_ADDRESS_WRAP;
	spp.AddressV       = D3D11_TEXTURE_ADDRESS_WRAP;
	spp.AddressW       = D3D11_TEXTURE_ADDRESS_WRAP;
	spp.MipLODBias     = 0.0f;
	spp.MaxAnisotropy  = 1;
	spp.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	spp.BorderColor[0] = 0.0f;
	spp.BorderColor[1] = 0.0f;
	spp.BorderColor[2] = 0.0f;
	spp.BorderColor[3] = 0.0f;
	spp.MinLOD         = 0;
	spp.MaxLOD         = D3D11_FLOAT32_MAX;
	m_d3dDevice->CreateSamplerState(&spp,&m_d3dSamplerPoint);
}

void clkDirect3D11::depthStencilSet(int a_mode)
{
	switch(a_mode)
	{
	case CLK_DEPTHSTENCIL_ENABLE:
		depthStencilEnable();
		break;
	case CLK_DEPTHSTENCIL_DISABLE:
		depthStencilDisable();
		break;
	}
}

void clkDirect3D11::fullscreenEnable()
{
	m_d3dFullscreenMode = CLK_FULLSCREEN_ENABLE;
	m_dxgiSwapChain->SetFullscreenState(TRUE,0);
}

void clkDirect3D11::fullscreenDisable()
{
	m_d3dFullscreenMode = CLK_FULLSCREEN_DISABLE;
	m_dxgiSwapChain->SetFullscreenState(FALSE,0);
}

void clkDirect3D11::depthStencilEnable()
{
	m_d3dDepthStencilMode = CLK_DEPTHSTENCIL_ENABLE;
	m_d3dDevcon->OMSetDepthStencilState(m_d3dDepthStencilEnabled,0);
}

void clkDirect3D11::depthStencilDisable()
{
	m_d3dDepthStencilMode = CLK_DEPTHSTENCIL_DISABLE;
	m_d3dDevcon->OMSetDepthStencilState(m_d3dDepthStencilDisabled,0);
}

void clkDirect3D11::rasterizerSet(int a_mode)
{
	switch(a_mode)
	{
	case CLK_RASTERIZER_SOLID:
		rasterizerSolid();
		break;
	case CLK_RASTERIZER_WIREFRAME:
		rasterizerWireframe();
		break;
	}
}

void clkDirect3D11::rasterizerSolid()
{
	m_d3dRasterizerMode = CLK_RASTERIZER_SOLID;
	m_d3dDevcon->RSSetState(m_d3dRasterizerSolid);
}

void clkDirect3D11::rasterizerWireframe()
{
	m_d3dRasterizerMode = CLK_RASTERIZER_WIREFRAME;
	m_d3dDevcon->RSSetState(m_d3dRasterizerWireframe);
}

void clkDirect3D11::samplerSet(int a_mode)
{
	switch(a_mode)
	{
	case CLK_SAMPLER_POINT:
		samplerPoint();
		break;
	}
}
void clkDirect3D11::samplerPoint()
{
	m_d3dSamplerMode = CLK_SAMPLER_POINT;
	m_d3dDevcon->PSSetSamplers(0,1,&m_d3dSamplerPoint);
}

void clkDirect3D11::shutDirect3D11()
{
	//disable fullscreen mode
	if(m_dxgiSwapChain)
		fullscreenDisable();

	//release memory for font list
	for(int i=0;i<(int)m_d3dFontList.size();i++)
	{
		D3D_SAFEDELETE(m_d3dFontList[i]->m_font);
		delete m_d3dFontList[i];
		m_d3dFontList[i] = 0;
	}
	m_d3dFontList.clear();

	//release memory for mesh list
	for(int i=0;i<(int)m_d3dMeshList.size();i++)
	{
		for(int j=0;j<(int)m_d3dMeshList[i]->m_submeshList.size();j++)
		{
			D3D_SAFERELEASE(m_d3dMeshList[i]->m_submeshList[j]->m_vertexBuffer);
			delete m_d3dMeshList[i]->m_submeshList[j];
			m_d3dMeshList[i]->m_submeshList[j] = 0;
		}
		delete m_d3dMeshList[i];
		m_d3dMeshList[i] = 0;
	}
	m_d3dMeshList.clear();

	//release memory for shader list
	for(int i=0;i<(int)m_d3dShaderList.size();i++)
	{
		D3D_SAFERELEASE(m_d3dShaderList[i]->m_inputLayout);
		D3D_SAFERELEASE(m_d3dShaderList[i]->m_vertexShader);
		D3D_SAFERELEASE(m_d3dShaderList[i]->m_pixelShader);
		delete m_d3dShaderList[i];
		m_d3dShaderList[i] = 0;
	}
	m_d3dShaderList.clear();

	//release memory for texture list
	for(int i=0;i<(int)m_d3dTextureList.size();i++)
	{
		D3D_SAFERELEASE(m_d3dTextureList[i]->m_texture);
		D3D_SAFERELEASE(m_d3dTextureList[i]->m_indexBuffer);
		D3D_SAFERELEASE(m_d3dTextureList[i]->m_vertexBuffer);
		delete m_d3dTextureList[i];
		m_d3dTextureList[i] = 0;
	}
	m_d3dTextureList.clear();

	//safe release system resources
	D3D_SAFERELEASE(m_d3dConstantBuffer);
	D3D_SAFERELEASE(m_d3dLineVertexBuffer);
	D3D_SAFERELEASE(m_d3dDepthStencilEnabled);
	D3D_SAFERELEASE(m_d3dDepthStencilDisabled);
	D3D_SAFERELEASE(m_d3dRasterizerSolid);
	D3D_SAFERELEASE(m_d3dRasterizerWireframe);
	D3D_SAFERELEASE(m_d3dSamplerPoint);

	//safe release graphic interfaces
	D3D_SAFEDELETE(m_dtkSpriteBatch);
	D3D_SAFERELEASE(m_d3dBackBuffer);
	D3D_SAFERELEASE(m_d3dDepthBuffer);
	D3D_SAFERELEASE(m_d3dBackBufferTexture);
	D3D_SAFERELEASE(m_d3dDepthBufferTexture);
	D3D_SAFERELEASE(m_d3dDevice);
	D3D_SAFERELEASE(m_d3dDevcon);
	D3D_SAFERELEASE(m_dxgiSwapChain);
}

bool clkDirect3D11::createFont(clkFontInfo* a_font, char* a_path)
{
	//check for valid params
	if(!a_font || !a_path)
		return false;

	//generate valid path
	string vpath;
	vpath.append(m_d3dFontPath);
	vpath.append(a_path);

	//check for duplicates
	for(int i=0;i<(int)m_d3dFontList.size();i++)
	{
		if(!m_d3dFontList[i]->m_path.compare(vpath))
		{
			//register font information
			a_font->m_id = i;
			a_font->m_path = m_d3dFontList[i]->m_path.data();
			return true;
		}
	}
	
	//create font object
	clkFont* font = new clkFont;
	font->m_path = vpath;
	font->m_font = new SpriteFont(m_d3dDevice,toWString(font->m_path).c_str());

	//register font information
	a_font->m_id = m_d3dFontList.size();
	a_font->m_path = font->m_path.data();

	//add font to list
	m_d3dFontList.push_back(font);
	return true;
}

bool clkDirect3D11::createMesh(clkMeshInfo* a_mesh, char* a_path)
{
	//check for valid params
	if(!a_mesh || !a_path)
		return false;

	//generate valid path
	string vpath;
	vpath.append(m_d3dMeshPath);
	vpath.append(a_path);

	//check for duplicates
	for(int i=0;i<(int)m_d3dMeshList.size();i++)
	{
		if(!m_d3dMeshList[i]->m_path.compare(vpath))
		{
			//register font information
			a_mesh->m_id = i;
			a_mesh->m_path = m_d3dMeshList[i]->m_path.c_str();
			return true;
		}
	}

	//create mesh object
	clkMesh* mesh = new clkMesh;
	mesh->m_path = vpath;

	//parse COLLADA mesh file with XML loader
	clkCollada collada;
	clkColladaScene scene;
	
	collada.openDoc(vpath.data());
	collada.loadVertices(&scene);
	for(int i=0;i<(int)scene.m_meshes.size();i++)
	{
		float radius = CLK_FLOAT_MIN;
		clkSubmesh* sub = new clkSubmesh;
		for(int j=0;j<(int)scene.m_meshes[i]->m_verts.size();j++)
		{
			clkVertex cv = *((clkVertex*)&scene.m_meshes[i]->m_verts[j]);
			sub->m_vertexList.push_back(cv);

			V3DF v(cv.px,cv.py,cv.pz);
			radius = (v.magnitude()>radius)?v.magnitude():radius;
		}
		sub->m_radius = radius;
		mesh->m_submeshList.push_back(sub);
		delete scene.m_meshes[i];
	}
	collada.closeDoc();

	//create vertex buffer for mesh
	for(int i=0;i<(int)mesh->m_submeshList.size();i++)
	{
		clkSubmesh* smesh = mesh->m_submeshList[i];

		//create the mesh vertex
		D3D11_BUFFER_DESC bdv;
		ZeroMemory(&bdv,sizeof(bdv));
		bdv.Usage          = D3D11_USAGE_DYNAMIC;
		bdv.ByteWidth      = smesh->m_vertexList.size()*sizeof(clkVertex);
		bdv.BindFlags      = D3D11_BIND_VERTEX_BUFFER;
		bdv.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		m_d3dDevice->CreateBuffer(&bdv,0,&smesh->m_vertexBuffer);

		//map vertex
		D3D11_MAPPED_SUBRESOURCE msv;
		m_d3dDevcon->Map(smesh->m_vertexBuffer,0,D3D11_MAP_WRITE_DISCARD,0,&msv);
		memcpy(msv.pData,smesh->m_vertexList.data(),smesh->m_vertexList.size()*sizeof(clkVertex));
		m_d3dDevcon->Unmap(smesh->m_vertexBuffer,0);
	}

	//register mesh information
	a_mesh->m_id = m_d3dMeshList.size();
	a_mesh->m_path = mesh->m_path.data();

	m_d3dMeshList.push_back(mesh);
	return true;
}

bool clkDirect3D11::createShader(
	clkShaderInfo* a_shader,
	char* a_path,
	char* a_vsName,
	char* a_psName)
{
	//check for valid params
	if(!a_shader || !a_path)
		return false;

	//generate valid path
	string vpath;
	vpath.append(m_d3dShaderPath);
	vpath.append(a_path);

	//default shader names if none provided
	if(!a_vsName) a_vsName = "mainV";
	if(!a_psName) a_psName = "mainP";

	//check for duplicates
	for(int i=0;i<(int)m_d3dShaderList.size();i++)
	{
		if(m_d3dShaderList[i]->m_path.compare(vpath)
			&& !m_d3dShaderList[i]->m_vsName.compare(a_vsName)
			&& !m_d3dShaderList[i]->m_psName.compare(a_psName))
		{
			//register font information
			a_shader->m_id = i;
			a_shader->m_path = m_d3dShaderList[i]->m_path.data();
			a_shader->m_vsName = m_d3dShaderList[i]->m_vsName.data();
			a_shader->m_psName = m_d3dShaderList[i]->m_psName.data();
			return true;
		}
	}

	//detect appropriate shader compiler base on the feature level
	char* vprofile = 0;
	char* pprofile = 0;
	switch(m_d3dFeatureLevel)
	{
	case D3D_FEATURE_LEVEL_9_1:  vprofile = "vs_1_1"; pprofile = "ps_1_1"; break;
	case D3D_FEATURE_LEVEL_9_2:  vprofile = "vs_2_0"; pprofile = "ps_2_0"; break;
	case D3D_FEATURE_LEVEL_9_3:  vprofile = "vs_3_0"; pprofile = "ps_3_0"; break;
	case D3D_FEATURE_LEVEL_10_0: vprofile = "vs_4_0"; pprofile = "ps_4_0"; break;
	case D3D_FEATURE_LEVEL_10_1: vprofile = "vs_4_1"; pprofile = "ps_4_1"; break;
	case D3D_FEATURE_LEVEL_11_0: vprofile = "vs_5_0"; pprofile = "ps_5_0"; break;
	};

	//compile shaders
	ID3DBlob* vs = 0;
	ID3DBlob* ps = 0;
	D3DCompileFromFile(toWString(vpath).c_str(),0,0,a_vsName,vprofile,0,0,&vs,0);
	D3DCompileFromFile(toWString(vpath).c_str(),0,0,a_psName,pprofile,0,0,&ps,0);

	//create shader object
	clkShader* shader = new clkShader;
	shader->m_path = vpath;
	shader->m_vsName = a_vsName;
	shader->m_psName = a_psName;

	//create vertex shader
	m_d3dDevice->CreateVertexShader(
		vs->GetBufferPointer(),
		vs->GetBufferSize(),0,
		&shader->m_vertexShader);

	//create pixel shader
	m_d3dDevice->CreatePixelShader(
		ps->GetBufferPointer(),
		ps->GetBufferSize(),0,
		&shader->m_pixelShader);

	//define input layout
	D3D11_INPUT_ELEMENT_DESC layout[] =
	{
		{"POSITION", 0,DXGI_FORMAT_R32G32B32_FLOAT,0,0, D3D11_INPUT_PER_VERTEX_DATA,0},
		{"NORMAL",   0,DXGI_FORMAT_R32G32B32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0},
		{"TANGENT",  0,DXGI_FORMAT_R32G32B32_FLOAT,0,24,D3D11_INPUT_PER_VERTEX_DATA,0},
		{"BINORMAL", 0,DXGI_FORMAT_R32G32B32_FLOAT,0,36,D3D11_INPUT_PER_VERTEX_DATA,0},
		{"TEXCOORD", 0,DXGI_FORMAT_R32G32_FLOAT,   0,48,D3D11_INPUT_PER_VERTEX_DATA,0},
	};

	//create input layout
	m_d3dDevice->CreateInputLayout(
		layout,5,
		vs->GetBufferPointer(),
		vs->GetBufferSize(),
		&shader->m_inputLayout);

	//release memory
	vs->Release();
	ps->Release();

	//register shader information
	a_shader->m_id = m_d3dShaderList.size();
	a_shader->m_path = shader->m_path.data();
	a_shader->m_vsName = shader->m_vsName.data();
	a_shader->m_psName = shader->m_psName.data();

	//add shader to list
	m_d3dShaderList.push_back(shader);
	return true;
}

bool clkDirect3D11::createTexture(
	clkTextureInfo* a_texture,
	char* a_path,
	int a_center)
{
	//check for valid params
	if(!a_texture || !a_path)
		return false;

	//generate valid path
	string vpath;
	vpath.append(m_d3dTexturePath);
	vpath.append(a_path);

	//check for duplicates
	for(int i=0;i<(int)m_d3dTextureList.size();i++)
	{
		if(!m_d3dTextureList[i]->m_path.compare(vpath))
		{
			//register font information
			a_texture->m_id = i;
			a_texture->m_path = m_d3dTextureList[i]->m_path.data();
			a_texture->m_width = (float)m_d3dTextureList[i]->m_info.Width;
			a_texture->m_height = (float)m_d3dTextureList[i]->m_info.Height;
			return true;
		}
	}

	//create texture object
	clkTexture* texture = new clkTexture;
	texture->m_path = vpath;

	//load texture file
	ID3D11Resource* res2d = 0;
	ID3D11Texture2D* tex2d = 0;
	CreateWICTextureFromFile(m_d3dDevice,0,toWString(vpath).c_str(),&res2d,&texture->m_texture,0);
	res2d->QueryInterface(__uuidof(ID3D11Texture2D),(void**)&tex2d);
	tex2d->GetDesc(&texture->m_info);
	res2d->Release();
	tex2d->Release();

	//create texture quad
	float hw = (float)texture->m_info.Width/2.0f;
	float hh = (float)texture->m_info.Height/2.0f;

	int quadI[] = {0,1,2,2,1,3};
	clkVertex quadV[] =
	{
		{/*POSITION*/-hw,-hh,0,/*NORMAL*/0,0,1,/*TANGENT*/0,0,0,/*BINORMAL*/0,0,0,/*TEXCOORD*/0,1},
		{/*POSITION*/ hw,-hh,0,/*NORMAL*/0,0,1,/*TANGENT*/0,0,0,/*BINORMAL*/0,0,0,/*TEXCOORD*/1,1},
		{/*POSITION*/-hw, hh,0,/*NORMAL*/0,0,1,/*TANGENT*/0,0,0,/*BINORMAL*/0,0,0,/*TEXCOORD*/0,0},
		{/*POSITION*/ hw, hh,0,/*NORMAL*/0,0,1,/*TANGENT*/0,0,0,/*BINORMAL*/0,0,0,/*TEXCOORD*/1,0},
	};

	//center texture quad accordingly
	switch(a_center)
	{
	case CLK_TEXTURE_TOPLEFT:  for(int i=0;i<4;i++){quadV[i].px += hw;quadV[i].py -= hh;} break;
	case CLK_TEXTURE_TOPRIGHT: for(int i=0;i<4;i++){quadV[i].px -= hw;quadV[i].py -= hh;} break;
	case CLK_TEXTURE_BOTLEFT:  for(int i=0;i<4;i++){quadV[i].px += hw;quadV[i].py += hh;} break;
	case CLK_TEXTURE_BOTRIGHT: for(int i=0;i<4;i++){quadV[i].px -= hw;quadV[i].py += hh;} break;
	};

	//create texture quad index buffer
	D3D11_BUFFER_DESC bdi;
	ZeroMemory(&bdi,sizeof(bdi));
	bdi.Usage          = D3D11_USAGE_DYNAMIC;
	bdi.ByteWidth      = sizeof(quadI);
	bdi.BindFlags      = D3D11_BIND_VERTEX_BUFFER;
	bdi.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	m_d3dDevice->CreateBuffer(&bdi,0,&texture->m_indexBuffer);

	D3D11_MAPPED_SUBRESOURCE msi;
	m_d3dDevcon->Map(texture->m_indexBuffer,0,D3D11_MAP_WRITE_DISCARD,0,&msi);
	memcpy(msi.pData,quadI,sizeof(quadI));
	m_d3dDevcon->Unmap(texture->m_indexBuffer,0);

	//create texture quad vertex buffer
	D3D11_BUFFER_DESC bdv;
	ZeroMemory(&bdv,sizeof(bdv));
	bdv.Usage          = D3D11_USAGE_DYNAMIC;
	bdv.ByteWidth      = sizeof(quadV);
	bdv.BindFlags      = D3D11_BIND_VERTEX_BUFFER;
	bdv.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	m_d3dDevice->CreateBuffer(&bdv,0,&texture->m_vertexBuffer);

	D3D11_MAPPED_SUBRESOURCE msv;
	m_d3dDevcon->Map(texture->m_vertexBuffer,0,D3D11_MAP_WRITE_DISCARD,0,&msv);
	memcpy(msv.pData,quadV,sizeof(quadV));
	m_d3dDevcon->Unmap(texture->m_vertexBuffer,0);

	//register texture information
	a_texture->m_id = m_d3dTextureList.size();
	a_texture->m_path = texture->m_path.data();
	a_texture->m_width = (float)texture->m_info.Width;
	a_texture->m_height = (float)texture->m_info.Height;

	//add texture to list
	m_d3dTextureList.push_back(texture);
	return true;
}

void clkDirect3D11::drawMesh(
	clkMeshInfo* a_mesh,
	V3DF a_position,
	V3DF a_rotation,
	V3DF a_scaling,
	V3DF a_centering,
	clkColor a_color,
	int a_rotateType)
{
	//check for valid mesh, camera, and shader
	if(!a_mesh || !m_crCamera || !m_crShader) return;

	//obtain mesh object
	clkMesh* mesh = m_d3dMeshList[a_mesh->m_id];

	//compute world matrix
	XMFLOAT4X4 trn, rot, scl, world;
	XMStoreFloat4x4(&trn,XMMatrixTranslation(a_position.x,a_position.y,a_position.z));
	XMStoreFloat4x4(&scl,XMMatrixScaling(a_scaling.x,a_scaling.y,a_scaling.z));

	switch(a_rotateType)
	{
	case CLK_ROTATE_DEGREE:
		XMStoreFloat4x4(&rot,XMMatrixRotationQuaternion(XMQuaternionRotationRollPitchYaw(
			CLK_RADIAN(a_rotation.x),CLK_RADIAN(a_rotation.y),CLK_RADIAN(a_rotation.z))));
		break;
	case CLK_ROTATE_RADIAN:
		XMStoreFloat4x4(&rot,XMMatrixRotationQuaternion(XMQuaternionRotationRollPitchYaw(
			a_rotation.x,a_rotation.y,a_rotation.z)));
		break;
	case CLK_ROTATE_LOOKAT:
		{
			XMFLOAT3 ups(0,1,0);
			XMFLOAT3 pos(a_position.x,a_position.y,a_position.z);
			XMFLOAT3 eye(a_rotation.x,a_rotation.y,a_rotation.z);

			XMStoreFloat4x4(
				&rot,XMMatrixLookAtLH(
				XMLoadFloat3(&pos),
				XMLoadFloat3(&eye),
				XMLoadFloat3(&ups)));

			XMStoreFloat4x4(&rot,XMMatrixInverse(0,XMLoadFloat4x4(&rot)));

			rot._14 = 0;
			rot._24 = 0;
			rot._34 = 0;
			rot._41 = 0;
			rot._42 = 0;
			rot._43 = 0;
			rot._44 = 1;
		}
		break;
	case CLK_ROTATE_LOOKTO:
		{
			XMFLOAT3 ups(0,1,0);
			XMFLOAT3 pos(a_position.x,a_position.y,a_position.z);
			XMFLOAT3 eye(a_rotation.x,a_rotation.y,a_rotation.z);

			XMStoreFloat4x4(
				&rot,XMMatrixLookToLH(
				XMLoadFloat3(&pos),
				XMLoadFloat3(&eye),
				XMLoadFloat3(&ups)));

			XMStoreFloat4x4(&rot,XMMatrixInverse(0,XMLoadFloat4x4(&rot)));

			rot._14 = 0;
			rot._24 = 0;
			rot._34 = 0;
			rot._41 = 0;
			rot._42 = 0;
			rot._43 = 0;
			rot._44 = 1;
		}
		break;
	}

	XMStoreFloat4x4(&world,XMMatrixMultiply(XMMatrixMultiply(XMLoadFloat4x4(&scl),
		XMLoadFloat4x4(&rot)),XMLoadFloat4x4(&trn)));

	//send updated constant buffer to GPU
	clkConstantBuffer cbuf;
	cbuf.m_color = XMFLOAT4A(a_color.r,a_color.g,a_color.b,a_color.a);
	cbuf.m_eye = XMFLOAT4A(m_crCamera->m_eye.x,m_crCamera->m_eye.x,m_crCamera->m_eye.x,0);
	cbuf.m_worldMatrix = world;
	cbuf.m_viewMatrix = m_crCamera->m_viewMatrix;
	cbuf.m_perspectiveMatrix = m_crCamera->m_perspectiveMatrix;
	cbuf.m_orthogonalMatrix = m_crCamera->m_orthogonalMatrix;
	m_d3dDevcon->UpdateSubresource(m_d3dConstantBuffer,0,0,&cbuf,0,0);

	//loop through and draw each subset meshes
	UINT offset = 0;
	UINT stride = sizeof(clkVertex);
	for(int i=0;i<(int)mesh->m_submeshList.size();i++)
	{
		//obtain submesh object
		clkSubmesh* smesh = mesh->m_submeshList[i];

		//test culling
		if(m_crCamera)
		{
			bool draw = true;
			for(int i=0;i<6;i++)
			{
				XMFLOAT4 dotProduct;
				float scaling = (a_scaling.x>a_scaling.y)?a_scaling.x:
					(a_scaling.y>a_scaling.z)?a_scaling.y:a_scaling.z;
				XMStoreFloat4(&dotProduct,XMPlaneDotCoord(XMLoadFloat4(&m_crCamera->m_frustum[i]),
					XMLoadFloat3(&XMFLOAT3(a_position.x,a_position.y,a_position.z))));
				if(dotProduct.x + (smesh->m_radius * scaling) < 0.0f)
				{
					draw = false;
					break;
				}
			}

			if(!draw) continue;
		}

		//set the texture resources
		if(m_crTexture)
			m_d3dDevcon->PSSetShaderResources(0,1,&m_crTexture->m_texture);

		//set vertex buffer and draw the submesh
		m_d3dDevcon->IASetVertexBuffers(0,1,&smesh->m_vertexBuffer,&stride,&offset);
		m_d3dDevcon->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
		m_d3dDevcon->Draw(smesh->m_vertexList.size(),0);
	}
}

void clkDirect3D11::drawTexture(
	clkTextureInfo* a_texture,
	V3DF a_position,
	V3DF a_rotation,
	V3DF a_scaling,
	V3DF a_centering,
	clkColor a_color)
{
	//check for valid mesh and camera
	if(!a_texture || !m_crCamera) return;

	//obtain texture object
	clkTexture* texture = m_d3dTextureList[a_texture->m_id];

	//compute world matrix
	XMFLOAT4X4 trn, rot, scl, world;
	XMStoreFloat4x4(&trn,XMMatrixTranslation(a_position.x,-a_position.y,a_position.z));
	XMStoreFloat4x4(&rot,XMMatrixRotationQuaternion(XMQuaternionRotationRollPitchYaw(
		CLK_RADIAN(a_rotation.x),CLK_RADIAN(a_rotation.y),CLK_RADIAN(a_rotation.z))));
	XMStoreFloat4x4(&scl,XMMatrixScaling(a_scaling.x,a_scaling.y,a_scaling.z));
	XMStoreFloat4x4(&world,XMMatrixMultiply(XMMatrixMultiply(XMLoadFloat4x4(&scl),
		XMLoadFloat4x4(&rot)),XMLoadFloat4x4(&trn)));

	//send updated constant buffer to GPU
	clkConstantBuffer cbuf;
	cbuf.m_color = XMFLOAT4A(a_color.r,a_color.g,a_color.b,a_color.a);
	cbuf.m_eye = XMFLOAT4A(m_crCamera->m_eye.x,m_crCamera->m_eye.x,m_crCamera->m_eye.x,0);
	cbuf.m_worldMatrix = world;
	cbuf.m_viewMatrix = m_crCamera->m_viewMatrix;
	cbuf.m_perspectiveMatrix = m_crCamera->m_perspectiveMatrix;
	cbuf.m_orthogonalMatrix = m_crCamera->m_orthogonalMatrix;
	m_d3dDevcon->UpdateSubresource(m_d3dConstantBuffer,0,0,&cbuf,0,0);

	//set texture, index, and vertex buffer
	UINT offset = 0;
	UINT stride = sizeof(clkVertex);
	m_d3dDevcon->PSSetShaderResources(0,1,&texture->m_texture);
	m_d3dDevcon->IASetVertexBuffers(0,1,&texture->m_vertexBuffer,&stride,&offset);
	m_d3dDevcon->IASetIndexBuffer(texture->m_indexBuffer,DXGI_FORMAT_R32_UINT,0);
	m_d3dDevcon->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	//disable depth stencil then draw texture and enable back depth stencil
	depthStencilDisable();
	m_d3dDevcon->DrawIndexed(6,0,0);
	depthStencilEnable();
}

void clkDirect3D11::drawLine(
	V3DF a_start,
	V3DF a_end,
	clkColor a_color)
{
	//check for valid camera
	if(!m_crCamera) return;

	//construct line
	clkVertex line[] =
	{
		{a_start.x,a_start.y,a_start.z,0,0,0,0,0},
		{a_end.x,a_end.y,a_end.z,0,0,0,0,0},
	};

	//send updated constant buffer to GPU
	clkConstantBuffer cbuf;
	memset(&cbuf,0,sizeof(clkConstantBuffer));

	XMFLOAT4X4 identity;
	XMStoreFloat4x4(&identity,XMMatrixIdentity());

	cbuf.m_color = XMFLOAT4A(a_color.r,a_color.g,a_color.b,a_color.a);
	cbuf.m_worldMatrix = identity;
	cbuf.m_viewMatrix = m_crCamera->m_viewMatrix;
	cbuf.m_perspectiveMatrix = m_crCamera->m_perspectiveMatrix;
	cbuf.m_orthogonalMatrix = m_crCamera->m_orthogonalMatrix;
	m_d3dDevcon->UpdateSubresource(m_d3dConstantBuffer,0,0,&cbuf,0,0);

	D3D11_MAPPED_SUBRESOURCE msv;
	m_d3dDevcon->Map(m_d3dLineVertexBuffer,0,D3D11_MAP_WRITE_DISCARD,0,&msv);
	memcpy(msv.pData,line,sizeof(line));
	m_d3dDevcon->Unmap(m_d3dLineVertexBuffer,0);

	//draw line
	UINT offset = 0;
	UINT stride = sizeof(clkVertex);
	m_d3dDevcon->IASetVertexBuffers(0,1,&m_d3dLineVertexBuffer,&stride,&offset);
	m_d3dDevcon->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP);
	m_d3dDevcon->Draw(2,0);
}

void clkDirect3D11::drawText(
	string a_text,
	V2DF a_position,
	V2DF a_scaling,
	V2DF a_centering,
	float a_rotation,
	clkColor a_color)
{
	//check for valid font
	if(!m_crFont) return;

	//setup text info
	XMFLOAT2 ori((float*)&a_centering);
	XMFLOAT2 pos((float*)&a_position);
	XMFLOAT2 scl((float*)&a_scaling);
	XMFLOAT4 clr((float*)&a_color);

	//draw text
	m_dtkSpriteBatch->Begin();
	m_crFont->m_font->DrawString(
		m_dtkSpriteBatch,
		toWString(a_text).c_str(),
		XMLoadFloat2(&pos),
		XMLoadFloat4(&clr),
		a_rotation,
		XMLoadFloat2(&ori),
		XMLoadFloat2(&scl),
		SpriteEffects_None,0);
	m_dtkSpriteBatch->End();

	//restore states
	m_d3dDevcon->VSSetConstantBuffers(0,1,&m_d3dConstantBuffer);
	depthStencilSet(m_d3dDepthStencilMode);
	rasterizerSet(m_d3dRasterizerMode);
	if(m_crShader)
	{
		m_d3dDevcon->VSSetShader(m_crShader->m_vertexShader,0,0);
		m_d3dDevcon->PSSetShader(m_crShader->m_pixelShader,0,0);
		m_d3dDevcon->IASetInputLayout(m_crShader->m_inputLayout);
	}
}

void clkDirect3D11::drawClear(clkColor a_color)
{
	//clear render target with given color
	//clear depth stencil buffer
	m_d3dDevcon->ClearRenderTargetView(m_d3dBackBuffer,(float*)&a_color);
	m_d3dDevcon->ClearDepthStencilView(m_d3dDepthBuffer,D3D11_CLEAR_DEPTH,1.0f,0);
}

void clkDirect3D11::drawPresent()
{
	//present the final frame
	m_dxgiSwapChain->Present(0,0);
}

void clkDirect3D11::setFont(clkFontInfo* a_font)
{
	m_crFont = (a_font)?m_d3dFontList[a_font->m_id]:0;
}

void clkDirect3D11::setShader(clkShaderInfo* a_shader)
{
	m_crShader = (a_shader)?m_d3dShaderList[a_shader->m_id]:0;
	if(m_crShader)
	{
		m_d3dDevcon->VSSetShader(m_crShader->m_vertexShader,0,0);
		m_d3dDevcon->PSSetShader(m_crShader->m_pixelShader,0,0);
		m_d3dDevcon->IASetInputLayout(m_crShader->m_inputLayout);
	}
}

void clkDirect3D11::setTexture(clkTextureInfo* a_texture)
{
	m_crTexture = (a_texture)?m_d3dTextureList[a_texture->m_id]:0;
}

void clkDirect3D11::setCamera(clkCamera* a_camera)
{
	m_crCamera = (a_camera)?a_camera:0;
}